Skip to main content

intro

自己开发用户管理系统是一件比较麻烦的事情,比如密码验证OAuth 登录邮件收发密码找回安全模块等等,但是这些都是可以高度复用的模块,只有具体的数据库应该才是需要设计的,除非有特殊合规或极端定制需求,一般不建议从零实现。因此我们要找的正是 “开源/免费、模块化、可嵌入不同后端框架” 的方案。

(一) “嵌模块” 和 “独立身份服务”

路线 A:独立身份服务

把登录、密码、OAuth、邮件找回、MFA、会话、令牌签发等都交给一个专门的 Identity Server(你当它是“自建 Auth0”)。

优点

  • 功能全、成熟、坑少(密码学、会话、令牌、刷新、撤销、MFA、风控)
  • 多端一致:Web/iOS/Android/桌面都走 OIDC/OAuth2
  • 你的业务后端只做“授权校验 + userId”即可

缺点

  • 需要部署与运维一个额外服务
  • 要理解 OIDC/OAuth2 基本概念(但长期一定值得)

典型架构

  • Identity:OIDC Provider(发 JWT / session)
  • API 后端:验证 JWT / introspection,做 RBAC/ABAC
  • WebSocket:用 access token 建链鉴权

路线 B:在后端里装 auth 模块

比如在 Flask/NestJS 里加一个 auth 插件/库。

优点

  • 部署更简单(一个服务)
  • 对单体项目、早期 MVP 友好

缺点

  • OAuth2/OIDC、邮件验证、找回、MFA、风控等要么缺、要么拼装
  • 将来多端和扩展时更容易“长成一团”

(二) 路线 A 开源方案

我们按 路线A 进行

方案 1:Keycloak(强推荐)

定位:开源自托管 Identity & Access Management(OIDC/OAuth2/SAML)

  • 模块化:是(身份服务独立)
  • 适配后端:任何语言/框架都可(只要会验 JWT / OIDC)
  • 功能:注册登录、邮箱验证、找回、OAuth 社交登录、MFA、RBAC、会话管理、用户管理后台、审计等

适合你:你要做多端同步软件,后面一定有 iOS/Android/Windows,Keycloak 是“最省心的自建身份中枢”

方案 2:Ory(Ory Kratos/Hydra/Keto 等,组件化更强)

定位:更“云原生”的模块拼装套件。

  • Kratos:身份(注册/登录/找回/验证)
  • Hydra:OAuth2/OIDC Provider
  • Keto:授权(RBAC/ABAC 类)
  • 模块化:极强(按组件组合)
  • 适配后端:任何语言/框架
  • 缺点:学习曲线较 Keycloak 更陡,需要你更懂身份系统边界

适合你:如果你偏“工程洁癖”、想把 auth 做成可组合组件,Ory 很强。

方案 3:Authentik(自托管,体验友好)

定位:更偏“易用 + 管理界面”的身份服务,支持 OIDC/SAML 等。

  • 模块化:是(独立服务)
  • 适配后端:广泛
  • 优点:上手部署和管理体验不错

适合你:想要比 Keycloak 更轻一些、界面友好一些的自托管选择。


(三) 路线 B 开源方案

如果你暂时不想引入独立身份服务,而是快速做 MVP:

Node(NestJS / Express)

  • Passport.js(策略式认证:OAuth、JWT、session)
  • Lucia / NextAuth(更偏 Web app,但对纯 API 要看你架构)
  • 缺点:邮件验证、找回、风控等仍需拼装

Flask / Python

  • Flask-Login(session 登录)
  • Authlib(OAuth2 client/server)
  • Flask-Security-Too(登录、注册、角色等)
  • Django 的话:Django Allauth、Djoser 等生态更成熟(但你不一定要换)

这条路的现实结论是: 能做,但很快会变成“多个库拼起来 + 自己补齐安全细节”,多端扩展与审计/风控会更累。

(四) 认证流程

1. 数据存放

思考:如果使用 Keycloak ,认证服务器 和 后端业务服务器上,分别要存放什么数据?

Keycloak 主要存的是“身份相关”的数据,也就是能回答这些问题的数据:

  • 这个用户是谁(userId/sub
  • 怎么登录(密码哈希、社交账号绑定、MFA)
  • 会话与令牌(session、refresh token、撤销)
  • 基本属性(email、emailVerified、username)
  • 组/角色(realm role、client role、groups)
  • 一些轻量的自定义属性(attributes,可存少量键值)

业务后端数据库(Postgres 之类)存核心数据——比如:

  • tables(作业/科研等)
  • tasks/bars
  • orderKey、折叠状态
  • 番茄钟 session、白噪音配置
  • 共享/协作权限(如果你做)
  • 用户偏好(主题、默认番茄时长等)

2. 认证流程

具体的认证流程如下:Keycloak 只管认证(AuthN),业务侧管用户档案

  • Keycloak:登录、签 token
  • 业务 DB:users 表 + 业务资料 + 全部业务实体

流程

  1. 前端登录 Keycloak → 得到 access_token
  2. 调你后端 /me(带 token)
  3. 后端验证 token(签名/issuer/audience)
  4. 从 token 里取 sub(Keycloak userId)
  5. sub 去业务库 users.keycloak_sub 查/创建用户(首次登录自动建档)

业务库里 users 表通常长这样:

  • id(业务用户主键,UUID)
  • keycloak_sub(唯一,来自 token 的 sub
  • email(可从 token 同步,或每次读 token)
  • display_name(业务侧可改)
  • created_atlast_seen_at
  • 业务偏好字段(默认番茄时长、主题等)

3. JWT 的内容

Keycloak 发的 JWT(access token)里最重要的是:

  • sub:用户唯一 ID(你用来做业务侧 user 绑定)
  • email / preferred_username:可选用于展示/同步
  • realm_access.roles / resource_access:角色(做粗粒度授权)
  • exp:过期时间(后端校验)

你的业务表(tables/tasks/pomodoro_sessions)都应该有:

  • user_id(业务用户 id 或直接用 sub

两种常见做法:

  • 做法 1(推荐):业务库用自己的 user_id(UUID),users 表里存 keycloak_sub 映射
  • 做法 2(简化):直接用 sub 当作业务侧 user_id(省一层映射,但将来换身份系统会更耦合)

思考:会话更新/刷新是 Keycloak 的事,那你的后端还需要存 session 吗?

一般不需要。你后端是“资源服务器”,校验 access token 即可。刷新 token、续期、注销(撤销)是 Keycloak 的职责

你后端只需要:

  • 校验 JWT
  • 对 exp 过期返回 401,让前端去刷新
  • WebSocket 场景:token 过期后要求客户端重连或发新 token

(五) 特殊流程

思考:一些特殊的用户身份关联的业务,比如密码找回,邮件认证,手机号关联等等,在哪里进行?

1. 密码找回(Password Reset)

Keycloak 做

原因:找回流程涉及

  • 校验邮箱/身份
  • 生成一次性 token / action link
  • 设置新密码(密码策略、哈希、风控)
  • 失效旧会话/刷新令牌(可选)

这些都是 IAM 的强项,不要让业务后端碰“密码学+安全边界”。

业务后端通常不用参与,最多是:用户找回后重新登录,业务侧照常从 token 的 sub 识别用户。


2. 邮箱认证(Email Verification)

在 Keycloak 做。Keycloak 能做:

  • 注册后发送验证邮件(verification link)
  • 记录 emailVerified=true/false
  • 配置“未验证邮箱不允许登录/不允许某些客户端访问”(取决于策略)

业务后端怎么知道“已验证”?

三种常见方式(从简单到完善):

  1. 每次请求从 JWT 里读 email_verified/emailVerified 类 claim
    • 最简单,实时性取决于 token 刷新(access token 生命周期通常 5–15 分钟)
  2. 业务侧在关键操作时调用 Keycloak 用户信息端点(userinfo/introspection)做强校验
    • 更强一致,但多一次网络请求
  3. Keycloak 发事件 → 业务后端订阅(Event Listener / Webhook / 消息队列)
    • 最工程化:验证通过立刻通知业务,适合“验证后立刻解锁某能力”的场景

对小项目来说:先用方式 1 足够;如果你有“验证后解锁同步/团队共享”等强需求,再做方式 3。


3. 手机号关联 / 短信验证码(SMS OTP)

这里需要分两类,因为“手机号”在很多产品里既是身份因子也可能是业务资料

A. 手机号作为登录方式或二次验证(身份因子)

建议放 Keycloak(身份服务),但现实是: Keycloak 原生对短信 OTP 不如邮箱那样“开箱即用”,通常需要:

  • 自定义认证流程(Authentication Flow)
  • 插件(SPI / Authenticator)
  • 接短信服务商(Twilio、AWS SNS、阿里云短信等)

如果你要做“手机号登录/短信找回/短信二次验证”,放 Keycloak 是对的,只是实现成本比邮箱大。

B. 手机号只是个人资料(Profile)

如果只是:

  • 你想在个人资料页显示手机号
  • 或用于客服联系、通知偏好 这类可以存业务库(users.profile)里。

实用建议: 你第一版产品(效率工具)通常不需要手机号。 优先:邮箱 + OAuth(Google/Apple)即可。


4. 账号绑定(Account Linking)

例如:用户先用 Google 登录,后来想绑定邮箱密码;或 Apple + Google 合并为一个账号。

Keycloak 做主导(它本来就管理“同一用户有哪些身份凭据/外部身份提供方”)。业务后端只关心一个稳定的 sub

只在一种情况需要业务参与:

你想做“两个不同 Keycloak 用户合并为一个业务用户”(真正的合并)。

这会牵涉业务数据迁移,通常是高级功能,MVP 不做。